/* BinderHanlder.m:
 * You may freely copy, distribute, and reuse the code in this example.
 * NeXT disclaims any warranty of any kind, expressed or  implied, as to its
 * fitness for any particular use.
 *
 * Written by: Mai Nguyen, NeXT Developer Support
 *
 *
 */
 
#import "BinderHandler.h"
#import <appkit/appkit.h>
#import <dbkit/dbkit.h>
#import <libc.h>

	/* Define localized strings */
#define FAILURE NXLocalizedString("Failure:", NULL, "Message given to user when an operation has failed.")
#define CANNOT_CONNECT NXLocalizedString("Couldn't connect to database", NULL, "Message given to user to explain what fails. ")
#define OK NXLocalizedString("OK", NULL, "Okay to continue ")
#define NO_BINDER_OBJECT NXLocalizedString("Binder object not properly allocated", NULL, "Message given to user about a system problem.")

	/* Global to be used by Author object */
static id myHandler;
@implementation BinderHandler

- appDidInit:sender
{
	/* Connect to the Sybase Adaptor */
	if ( myDatabase == nil ) {
		myDatabase = [[DBDatabase alloc]init];
		[myDatabase connectUsingAdaptor:"SybaseAdaptor"
								 andString:"sa@SYBASE/pubs"];
		}
	
		/* TESTING */
	if (![myDatabase isConnected]) {
		NXRunAlertPanel(FAILURE, CANNOT_CONNECT,OK, NULL, NULL);
		return self;
		}
	[myDatabase setDelegate:self];
	authorEntity = [myDatabase entityNamed:"authors"];

	myHandler = self;
 
  	return self;
}

/* Stuff the binder with attributes from the authors table
 */
- initBinder
{
	
	authorPrototype = [[Author alloc]init];
	propertyList = [[List alloc] init];
	containerList = [[List alloc] init];
	myBinder = [[DBBinder alloc] init];
	
	[myBinder setDatabase:myDatabase];
	[myBinder setProperties:propertyList];
	[myBinder setRecordPrototype:authorPrototype];

	[myBinder associateRecordIvar:"first"
        withProperty: [[DBExpression alloc] initForEntity:authorEntity
                                            fromDescription:"au_fname"]];
  	[myBinder associateRecordIvar:"last" 
        withProperty: [[DBExpression alloc] initForEntity:authorEntity
                                            fromDescription:"au_lname"]];
	[myBinder associateRecordIvar:"address"
		withProperty:[[DBExpression alloc] initForEntity:authorEntity
											fromDescription:"address"]];
	[myBinder associateRecordIvar:"state"
		withProperty:[[DBExpression alloc] initForEntity:authorEntity
											fromDescription:"state"]];
	[myBinder associateRecordIvar:"zip"
		withProperty:[[DBExpression alloc] initForEntity:authorEntity
											fromDescription:"zip"]];
	[myBinder associateRecordIvar:"phone"
		withProperty:[[DBExpression alloc] initForEntity:authorEntity
											fromDescription:"phone"]];
 	
	return self;
}


- free
{	
	if (authorPrototype)
		[authorPrototype free];
	if (propertyList)
		[propertyList free];
	if (containerList)
		[containerList free];
	if (myBinder)
		[myBinder free];

	return [super free];
}
		
- showAllRecords:sender
{
	[self initBinder];
	[self findAllRecords];

	return self;
}


- findAllRecords
{
	int  recordCount;
	
	if ( myBinder == nil )	{
		NXRunAlertPanel(FAILURE, NO_BINDER_OBJECT,
			"Binder object not properly allocated",
				"OK", NULL, NULL);
		return self;
	}
		
	[myBinder setContainer:containerList];		
	[myBinder select];
	
 	recordCount = [containerList count];
	sprintf(buf, "\n------------------------\n");
	[self appendToText:buf];
	sprintf(buf, "%s\t%d\n", "Total Number of Records:", recordCount);
	[self appendToText:buf];
  	[containerList makeObjectsPerform:@selector(printSelf)];

	return self;
}


/* Build a simple SQL query with DBQualifier and retrieve the data via the
 * binder object.
 */
- findByName:sender
{
	const char * name;
	id			 aProp;
	id			 theQualifier;
	
	name = [lastNameField stringValue];
	
	aProp = [[DBExpression alloc] initForEntity:authorEntity
                                            fromDescription:"au_lname"];
	theQualifier = [[DBQualifier allocFromZone:[self zone] ] 
		initForEntity:authorEntity
		fromDescription:"%@ LIKE %s", aProp, name];

		/* Must allocate a new binder object */	
	[self initBinder];
	[myBinder setQualifier:theQualifier];
	[self findAllRecords];
	[aProp free];
	[theQualifier free];
	return self;
}


- findByState:sender
{
	const char * state;
	id			 aProp;
	id			 theQualifier;
		
	
	state = [stateField stringValue];
	
	aProp = [[DBExpression alloc] initForEntity:authorEntity
                                            fromDescription:"state"];
	theQualifier = [[DBQualifier allocFromZone:[self zone] ] 
		initForEntity:authorEntity
		fromDescription:"%@ LIKE %s", aProp, state];
		
		/* Must allocate a new binder object with a different qualifier */
	[self initBinder];
	[myBinder setQualifier:theQualifier];
	[self findAllRecords];
	[aProp free];
	[theQualifier free];
	return self;
}


/* Appends the string passed to the doc view of the text view
 */
- appendToText:(const char *)newText
{
	int currentLength = [theTextView textLength];
	[theTextView setSel:currentLength :currentLength];
	[theTextView replaceSel:newText];
	[theTextView scrollSelToVisible];
	return self;
}


/* DBDatabase delegate methods to log error messages and SQL queries */

- (BOOL)db:aDb willEvaluateString:(const unsigned char*)aString usingBinder:aBinder
{
	[self appendToText:"SQL Query:\n"];
	[self appendToText:aString];
	[self appendToText:"\n"];
	return YES;
}

@end

@implementation Author

/* copyFromZone: is VERY important...this is how the prototype
 *  object is turned into records!
 */

- copyFromZone:(NXZone*)z
{
  Author *theCopy = [[Author allocFromZone:z] init];
  theCopy->first = NXCopyStringBufferFromZone(first, z);
  theCopy->last = NXCopyStringBufferFromZone(last, z);
  theCopy->address = NXCopyStringBufferFromZone(address, z);
  theCopy->state = NXCopyStringBufferFromZone(state, z);
  theCopy->zip = NXCopyStringBufferFromZone(zip, z);
  theCopy->phone = NXCopyStringBufferFromZone(phone, z);
  return theCopy;
}


/* Print data stored in the author object
 */ 
- printSelf
{
 char buf[200];
 
 sprintf(buf, "%s %s\n%s \t%s %s\n%s\n", first, last, address, state, zip,
 																	 phone);
 if (myHandler != nil)
 	[myHandler appendToText: buf];
  return self;
}


@end
